﻿// Inner Fire 游戏引擎库
// 场景分装 - 动态天空盒
//
// Copyright (c) 2023 by 尔西大帝. All rights reserved.
//
// 这个类分装各种场景的构建（都在一个文件里面太大了）
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-28

#include <dx_graphics.h>

namespace ifire {

void DxGraphics::DynamicCube_Update() {

  //
  // Animate the skull around the center sphere.
  //
  XMMATRIX skullScale = XMMatrixScaling(0.2f, 0.2f, 0.2f);
  XMMATRIX skullOffset = XMMatrixTranslation(3.0f, 2.0f, 0.0f);
  XMMATRIX skullLocalRotate = XMMatrixRotationY(2.0f * g_timer.TotalTime());
  XMMATRIX skullGlobalRotate = XMMatrixRotationY(0.5f * g_timer.TotalTime());
  XMStoreFloat4x4(&mSkullRitem->World,
      skullScale * skullLocalRotate * skullOffset * skullGlobalRotate);
  mSkullRitem->NumFramesDirty = gNumFrameResources;

  UpdateObjectCBs_V2();
  UpdateMaterialBuffer_V2();
  UpdateMainPassCB_V2();
  UpdateCubeMapFacePassCBs();
}

void DxGraphics::DynamicCube_InitPipeline() {
  mCamera.SetPosition(0.0f, 2.0f, -15.0f);
  BuildCubeFaceCamera(0.0f, 2.0f, 0.0f);
  mDynamicCubeMap = std::make_unique<CubeRenderTarget>(
      md3dDevice.Get(), CubeMapSize, CubeMapSize, DXGI_FORMAT_R8G8B8A8_UNORM);

  Cube_LoadTextures();
  Cube_BuildRootSignature();
  DynamicCube_BuildDescriptorHeaps();
  DynamicCube_BuildCubeDepthStencil();
  DynamicCube_BuildShadersAndInputLayout();
  Altar_BuildSkullGeometry();
  Altar_BuildShapeGeometry();
  DynamicCube_BuildMaterials();
  DynamicCube_BuildRenderItems();
  DynamicCube_BuildFrameResources();
  DynamicCube_BuildPSOs();
}

void DxGraphics::DynamicCube_Draw() {
  auto cmdListAlloc = mCurrFrameResource->CmdListAlloc;

  // Reuse the memory associated with command recording.
  // We can only reset when the associated command lists have finished execution
  // on the GPU.
  ThrowIfFailed(cmdListAlloc->Reset());

  // A command list can be reset after it has been added to the command queue
  // via ExecuteCommandList. Reusing the command list reuses memory.
  ThrowIfFailed(mCommandList->Reset(cmdListAlloc.Get(), mPSOs["opaque"].Get()));

  ID3D12DescriptorHeap* descriptorHeaps[] = {mSrvDescriptorHeap.Get()};
  mCommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);

  mCommandList->SetGraphicsRootSignature(mRootSignature.Get());

  // Bind all the materials used in this scene.  For structured buffers, we can
  // bypass the heap and set as a root descriptor.
  auto matBuffer = mCurrFrameResource->MaterialBuffer->Resource();
  mCommandList->SetGraphicsRootShaderResourceView(
      2, matBuffer->GetGPUVirtualAddress());

  // Bind the sky cube map.  For our demos, we just use one "world" cube map
  // representing the environment from far away, so all objects will use the
  // same cube map and we only need to set it once per-frame. If we wanted to
  // use "local" cube maps, we would have to change them per-object, or
  // dynamically index into an array of cube maps.

  CD3DX12_GPU_DESCRIPTOR_HANDLE skyTexDescriptor(
      mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());
  skyTexDescriptor.Offset(mSkyTexHeapIndex, mCbvSrvUavDescriptorSize);
  mCommandList->SetGraphicsRootDescriptorTable(3, skyTexDescriptor);

  // Bind all the textures used in this scene.  Observe
  // that we only have to specify the first descriptor in the table.
  // The root signature knows how many descriptors are expected in the table.
  mCommandList->SetGraphicsRootDescriptorTable(
      4, mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());

  DrawSceneToCubeMap();

  mCommandList->RSSetViewports(1, &mScreenViewport);
  mCommandList->RSSetScissorRects(1, &mScissorRect);

  // Indicate a state transition on the resource usage.
  auto to_render = CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
      D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
  mCommandList->ResourceBarrier(1, &to_render);

  // Clear the back buffer and depth buffer.
  auto dsv = DepthStencilView();
  auto back = CurrentBackBufferView();
  mCommandList->ClearRenderTargetView(back, Colors::LightSteelBlue, 0, nullptr);
  mCommandList->ClearDepthStencilView(dsv,
      D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);

  // Specify the buffers we are going to render to.
  mCommandList->OMSetRenderTargets(1, &back, true, &dsv);

  auto passCB = mCurrFrameResource->PassCB->Resource();
  mCommandList->SetGraphicsRootConstantBufferView(
      1, passCB->GetGPUVirtualAddress());

  // Use the dynamic cube map for the dynamic reflectors layer.
  CD3DX12_GPU_DESCRIPTOR_HANDLE dynamicTexDescriptor(
      mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());
  dynamicTexDescriptor.Offset(mSkyTexHeapIndex + 1, mCbvSrvUavDescriptorSize);
  mCommandList->SetGraphicsRootDescriptorTable(3, dynamicTexDescriptor);

  DrawRenderItems_V2(mCommandList.Get(),
      mRitemLayer[(int)RenderLayer::OpaqueDynamicReflectors]);

  // Use the static "background" cube map for the other objects (including the
  // sky)
  mCommandList->SetGraphicsRootDescriptorTable(3, skyTexDescriptor);

  DrawRenderItems_V2(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Opaque]);

  mCommandList->SetPipelineState(mPSOs["sky"].Get());
  DrawRenderItems_V2(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Sky]);

  // Indicate a state transition on the resource usage.
  auto to_present = CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
      D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
  mCommandList->ResourceBarrier(1, &to_present);

  // Done recording commands.
  ThrowIfFailed(mCommandList->Close());

  // Add the command list to the queue for execution.
  ID3D12CommandList* cmdsLists[] = {mCommandList.Get()};
  mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);

  // Swap the back and front buffers
  ThrowIfFailed(mSwapChain->Present(0, 0));
  mCurrBackBuffer = (mCurrBackBuffer + 1) % SwapChainBufferCount;

  // Advance the fence value to mark commands up to this fence point.
  mCurrFrameResource->Fence = ++mCurrentFence;

  // Add an instruction to the command queue to set a new fence point.
  // Because we are on the GPU timeline, the new fence point won't be
  // set until the GPU finishes processing all the commands prior to this
  // Signal().
  mCommandQueue->Signal(mFence.Get(), mCurrentFence);
}

void DxGraphics::DynamicCube_DrawRenderItems(ID3D12GraphicsCommandList* cmdList,
    const std::vector<RenderItem*>& ritems) {}

void DxGraphics::UpdateCubeMapFacePassCBs() {
  for (int i = 0; i < 6; ++i) {
    PassConstants cubeFacePassCB = mMainPassCB;

    XMMATRIX view = mCubeMapCamera[i].GetView();
    XMMATRIX proj = mCubeMapCamera[i].GetProj();

    XMMATRIX viewProj = XMMatrixMultiply(view, proj);
    auto det = XMMatrixDeterminant(view);
    XMMATRIX invView = XMMatrixInverse(&det, view);
    det = XMMatrixDeterminant(proj);
    XMMATRIX invProj = XMMatrixInverse(&det, proj);
    det = XMMatrixDeterminant(viewProj);
    XMMATRIX invViewProj = XMMatrixInverse(&det, viewProj);

    XMStoreFloat4x4(&cubeFacePassCB.View, XMMatrixTranspose(view));
    XMStoreFloat4x4(&cubeFacePassCB.InvView, XMMatrixTranspose(invView));
    XMStoreFloat4x4(&cubeFacePassCB.Proj, XMMatrixTranspose(proj));
    XMStoreFloat4x4(&cubeFacePassCB.InvProj, XMMatrixTranspose(invProj));
    XMStoreFloat4x4(&cubeFacePassCB.ViewProj, XMMatrixTranspose(viewProj));
    XMStoreFloat4x4(
        &cubeFacePassCB.InvViewProj, XMMatrixTranspose(invViewProj));
    cubeFacePassCB.EyePosW = mCubeMapCamera[i].GetPosition3f();
    cubeFacePassCB.RenderTargetSize =
        XMFLOAT2((float)CubeMapSize, (float)CubeMapSize);
    cubeFacePassCB.InvRenderTargetSize =
        XMFLOAT2(1.0f / CubeMapSize, 1.0f / CubeMapSize);

    auto currPassCB = mCurrFrameResource->PassCB.get();

    // Cube map pass cbuffers are stored in elements 1-6.
    currPassCB->CopyData(1 + i, cubeFacePassCB);
  }
}

void DxGraphics::BuildCubeFaceCamera(float x, float y, float z) {
  // Generate the cube map about the given position.
  XMFLOAT3 center(x, y, z);
  XMFLOAT3 worldUp(0.0f, 1.0f, 0.0f);

  // Look along each coordinate axis.
  XMFLOAT3 targets[6] = {
      XMFLOAT3(x + 1.0f, y, z), // +X
      XMFLOAT3(x - 1.0f, y, z), // -X
      XMFLOAT3(x, y + 1.0f, z), // +Y
      XMFLOAT3(x, y - 1.0f, z), // -Y
      XMFLOAT3(x, y, z + 1.0f), // +Z
      XMFLOAT3(x, y, z - 1.0f)  // -Z
  };

  // Use world up vector (0,1,0) for all directions except +Y/-Y.  In these
  // cases, we are looking down +Y or -Y, so we need a different "up" vector.
  XMFLOAT3 ups[6] = {
      XMFLOAT3(0.0f, 1.0f, 0.0f),  // +X
      XMFLOAT3(0.0f, 1.0f, 0.0f),  // -X
      XMFLOAT3(0.0f, 0.0f, -1.0f), // +Y
      XMFLOAT3(0.0f, 0.0f, +1.0f), // -Y
      XMFLOAT3(0.0f, 1.0f, 0.0f),  // +Z
      XMFLOAT3(0.0f, 1.0f, 0.0f)   // -Z
  };

  for (int i = 0; i < 6; ++i) {
    mCubeMapCamera[i].LookAt(center, targets[i], ups[i]);
    mCubeMapCamera[i].SetLens(0.5f * XM_PI, 1.0f, 0.1f, 1000.0f);
    mCubeMapCamera[i].UpdateViewMatrix();
  }
}

void DxGraphics::DynamicCube_BuildDescriptorHeaps() {
	//
	// Create the SRV heap.
	//
	D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
	srvHeapDesc.NumDescriptors = 6;
	srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
	srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
	ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(&mSrvDescriptorHeap)));

	//
	// Fill out the heap with actual descriptors.
	//
	CD3DX12_CPU_DESCRIPTOR_HANDLE hDescriptor(mSrvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());

    auto bricksTex = mTextures["bricksDiffuseMap"]->Resource;
    auto tileTex = mTextures["tileDiffuseMap"]->Resource;
    auto whiteTex = mTextures["defaultDiffuseMap"]->Resource;
    auto skyTex = mTextures["skyCubeMap"]->Resource;

	D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
	srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
	srvDesc.Format = bricksTex->GetDesc().Format;
	srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
	srvDesc.Texture2D.MostDetailedMip = 0;
	srvDesc.Texture2D.MipLevels = bricksTex->GetDesc().MipLevels;
	srvDesc.Texture2D.ResourceMinLODClamp = 0.0f;
	md3dDevice->CreateShaderResourceView(bricksTex.Get(), &srvDesc, hDescriptor);

	// next descriptor
	hDescriptor.Offset(1, mCbvSrvUavDescriptorSize);

	srvDesc.Format = tileTex->GetDesc().Format;
	srvDesc.Texture2D.MipLevels = tileTex->GetDesc().MipLevels;
	md3dDevice->CreateShaderResourceView(tileTex.Get(), &srvDesc, hDescriptor);

	// next descriptor
	hDescriptor.Offset(1, mCbvSrvUavDescriptorSize);

	srvDesc.Format = whiteTex->GetDesc().Format;
	srvDesc.Texture2D.MipLevels = whiteTex->GetDesc().MipLevels;
	md3dDevice->CreateShaderResourceView(whiteTex.Get(), &srvDesc, hDescriptor);

	// next descriptor
	hDescriptor.Offset(1, mCbvSrvUavDescriptorSize);

	srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
	srvDesc.TextureCube.MostDetailedMip = 0;
	srvDesc.TextureCube.MipLevels = skyTex->GetDesc().MipLevels;
	srvDesc.TextureCube.ResourceMinLODClamp = 0.0f;
	srvDesc.Format = skyTex->GetDesc().Format;
	md3dDevice->CreateShaderResourceView(skyTex.Get(), &srvDesc, hDescriptor);
	
	mSkyTexHeapIndex = 3;
	mDynamicTexHeapIndex = mSkyTexHeapIndex+1;

	auto srvCpuStart = mSrvDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
	auto srvGpuStart = mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart();
	auto rtvCpuStart = mRtvHeap->GetCPUDescriptorHandleForHeapStart();

	// Cubemap RTV goes after the swap chain descriptors.
	int rtvOffset = SwapChainBufferCount;

	CD3DX12_CPU_DESCRIPTOR_HANDLE cubeRtvHandles[6];
	for(int i = 0; i < 6; ++i)
		cubeRtvHandles[i] = CD3DX12_CPU_DESCRIPTOR_HANDLE(rtvCpuStart, rtvOffset + i, mRtvDescriptorSize);

	// Dynamic cubemap SRV is after the sky SRV.
	mDynamicCubeMap->BuildDescriptors(
		CD3DX12_CPU_DESCRIPTOR_HANDLE(srvCpuStart, mDynamicTexHeapIndex, mCbvSrvUavDescriptorSize),
		CD3DX12_GPU_DESCRIPTOR_HANDLE(srvGpuStart, mDynamicTexHeapIndex, mCbvSrvUavDescriptorSize),
		cubeRtvHandles);
}

void DxGraphics::DynamicCube_BuildCubeDepthStencil() {
  // Create the depth/stencil buffer and view.
  D3D12_RESOURCE_DESC depthStencilDesc;
  depthStencilDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
  depthStencilDesc.Alignment = 0;
  depthStencilDesc.Width = CubeMapSize;
  depthStencilDesc.Height = CubeMapSize;
  depthStencilDesc.DepthOrArraySize = 1;
  depthStencilDesc.MipLevels = 1;
  depthStencilDesc.Format = mDepthStencilFormat;
  depthStencilDesc.SampleDesc.Count = 1;
  depthStencilDesc.SampleDesc.Quality = 0;
  depthStencilDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
  depthStencilDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;

  D3D12_CLEAR_VALUE optClear;
  optClear.Format = mDepthStencilFormat;
  optClear.DepthStencil.Depth = 1.0f;
  optClear.DepthStencil.Stencil = 0;
  auto p1 = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
  ThrowIfFailed(md3dDevice->CreateCommittedResource(&p1, D3D12_HEAP_FLAG_NONE,
      &depthStencilDesc, D3D12_RESOURCE_STATE_COMMON, &optClear,
      IID_PPV_ARGS(mCubeDepthStencilBuffer.GetAddressOf())));

  // Create descriptor to mip level 0 of entire resource using the format of the
  // resource.
  md3dDevice->CreateDepthStencilView(
      mCubeDepthStencilBuffer.Get(), nullptr, mCubeDSV);

  // Transition the resource from its initial state to be used as a depth
  // buffer.
  auto b1 = CD3DX12_RESOURCE_BARRIER::Transition(mCubeDepthStencilBuffer.Get(),
      D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_DEPTH_WRITE);
  mCommandList->ResourceBarrier(1, &b1);
}

void DxGraphics::DynamicCube_BuildShadersAndInputLayout() {
  const D3D_SHADER_MACRO alphaTestDefines[] = {"ALPHA_TEST", "1", NULL, NULL};

  mShaders["standardVS"] = d3dUtil::CompileShader(
      "Shaders\\Default_Cube.hlsl", nullptr, "VS", "vs_5_1");
  mShaders["opaquePS"] = d3dUtil::CompileShader(
      "Shaders\\Default_Cube.hlsl", nullptr, "PS", "ps_5_1");

  mShaders["skyVS"] =
      d3dUtil::CompileShader("Shaders\\Sky.hlsl", nullptr, "VS", "vs_5_1");
  mShaders["skyPS"] =
      d3dUtil::CompileShader("Shaders\\Sky.hlsl", nullptr, "PS", "ps_5_1");

  mInputLayout = {
      {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
          D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
      {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12,
          D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
      {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24,
          D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
  };
}

void DxGraphics::DynamicCube_BuildSkullGeometry() {}

void DxGraphics::DynamicCube_BuildShapeGeometry() {}

void DxGraphics::DynamicCube_BuildMaterials() {
  auto bricks0 = std::make_unique<Material>();
  bricks0->Name = "bricks0";
  bricks0->MatCBIndex = 0;
  bricks0->DiffuseSrvHeapIndex = 0;
  bricks0->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  bricks0->FresnelR0 = XMFLOAT3(0.1f, 0.1f, 0.1f);
  bricks0->Roughness = 0.3f;

  auto tile0 = std::make_unique<Material>();
  tile0->Name = "tile0";
  tile0->MatCBIndex = 1;
  tile0->DiffuseSrvHeapIndex = 1;
  tile0->DiffuseAlbedo = XMFLOAT4(0.9f, 0.9f, 0.9f, 1.0f);
  tile0->FresnelR0 = XMFLOAT3(0.2f, 0.2f, 0.2f);
  tile0->Roughness = 0.1f;

  auto mirror0 = std::make_unique<Material>();
  mirror0->Name = "mirror0";
  mirror0->MatCBIndex = 2;
  mirror0->DiffuseSrvHeapIndex = 2;
  mirror0->DiffuseAlbedo = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
  mirror0->FresnelR0 = XMFLOAT3(0.98f, 0.97f, 0.95f);
  mirror0->Roughness = 0.1f;

  auto sky = std::make_unique<Material>();
  sky->Name = "sky";
  sky->MatCBIndex = 3;
  sky->DiffuseSrvHeapIndex = 3;
  sky->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  sky->FresnelR0 = XMFLOAT3(0.1f, 0.1f, 0.1f);
  sky->Roughness = 1.0f;

  auto skullMat = std::make_unique<Material>();
  skullMat->Name = "skullMat";
  skullMat->MatCBIndex = 4;
  skullMat->DiffuseSrvHeapIndex = 2;
  skullMat->DiffuseAlbedo = XMFLOAT4(0.8f, 0.8f, 0.8f, 1.0f);
  skullMat->FresnelR0 = XMFLOAT3(0.2f, 0.2f, 0.2f);
  skullMat->Roughness = 0.2f;

  mMaterials["bricks0"] = std::move(bricks0);
  mMaterials["tile0"] = std::move(tile0);
  mMaterials["mirror0"] = std::move(mirror0);
  mMaterials["sky"] = std::move(sky);
  mMaterials["skullMat"] = std::move(skullMat);
}

void DxGraphics::DynamicCube_BuildRenderItems() {
  auto skyRitem = std::make_unique<RenderItem>();
  XMStoreFloat4x4(&skyRitem->World, XMMatrixScaling(5000.0f, 5000.0f, 5000.0f));
  skyRitem->TexTransform = MathHelper::Identity4x4();
  skyRitem->ObjCBIndex = 0;
  skyRitem->Mat = mMaterials["sky"].get();
  skyRitem->Geo = mGeometries["shapeGeo"].get();
  skyRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  skyRitem->IndexCount = skyRitem->Geo->DrawArgs["sphere"].IndexCount;
  skyRitem->StartIndexLocation =
      skyRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
  skyRitem->BaseVertexLocation =
      skyRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;

  mRitemLayer[(int)RenderLayer::Sky].push_back(skyRitem.get());
  mAllRitems.push_back(std::move(skyRitem));

  auto skullRitem = std::make_unique<RenderItem>();
  skullRitem->World = MathHelper::Identity4x4();
  skullRitem->TexTransform = MathHelper::Identity4x4();
  skullRitem->ObjCBIndex = 1;
  skullRitem->Mat = mMaterials["skullMat"].get();
  skullRitem->Geo = mGeometries["skullGeo"].get();
  skullRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  skullRitem->IndexCount = skullRitem->Geo->DrawArgs["skull"].IndexCount;
  skullRitem->StartIndexLocation =
      skullRitem->Geo->DrawArgs["skull"].StartIndexLocation;
  skullRitem->BaseVertexLocation =
      skullRitem->Geo->DrawArgs["skull"].BaseVertexLocation;

  mSkullRitem = skullRitem.get();

  mRitemLayer[(int)RenderLayer::Opaque].push_back(skullRitem.get());
  mAllRitems.push_back(std::move(skullRitem));

  auto boxRitem = std::make_unique<RenderItem>();
  XMStoreFloat4x4(&boxRitem->World, XMMatrixScaling(2.0f, 1.0f, 2.0f) *
                                        XMMatrixTranslation(0.0f, 0.5f, 0.0f));
  XMStoreFloat4x4(&boxRitem->TexTransform, XMMatrixScaling(1.0f, 1.0f, 1.0f));
  boxRitem->ObjCBIndex = 2;
  boxRitem->Mat = mMaterials["bricks0"].get();
  boxRitem->Geo = mGeometries["shapeGeo"].get();
  boxRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  boxRitem->IndexCount = boxRitem->Geo->DrawArgs["box"].IndexCount;
  boxRitem->StartIndexLocation =
      boxRitem->Geo->DrawArgs["box"].StartIndexLocation;
  boxRitem->BaseVertexLocation =
      boxRitem->Geo->DrawArgs["box"].BaseVertexLocation;

  mRitemLayer[(int)RenderLayer::Opaque].push_back(boxRitem.get());
  mAllRitems.push_back(std::move(boxRitem));

  auto globeRitem = std::make_unique<RenderItem>();
  XMStoreFloat4x4(
      &globeRitem->World, XMMatrixScaling(2.0f, 2.0f, 2.0f) *
                              XMMatrixTranslation(0.0f, 2.0f, 0.0f));
  XMStoreFloat4x4(&globeRitem->TexTransform, XMMatrixScaling(1.0f, 1.0f, 1.0f));
  globeRitem->ObjCBIndex = 3;
  globeRitem->Mat = mMaterials["mirror0"].get();
  globeRitem->Geo = mGeometries["shapeGeo"].get();
  globeRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  globeRitem->IndexCount = globeRitem->Geo->DrawArgs["sphere"].IndexCount;
  globeRitem->StartIndexLocation =
      globeRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
  globeRitem->BaseVertexLocation =
      globeRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;

  mRitemLayer[(int)RenderLayer::OpaqueDynamicReflectors].push_back(
      globeRitem.get());
  mAllRitems.push_back(std::move(globeRitem));

  auto gridRitem = std::make_unique<RenderItem>();
  gridRitem->World = MathHelper::Identity4x4();
  XMStoreFloat4x4(&gridRitem->TexTransform, XMMatrixScaling(8.0f, 8.0f, 1.0f));
  gridRitem->ObjCBIndex = 4;
  gridRitem->Mat = mMaterials["tile0"].get();
  gridRitem->Geo = mGeometries["shapeGeo"].get();
  gridRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  gridRitem->IndexCount = gridRitem->Geo->DrawArgs["grid"].IndexCount;
  gridRitem->StartIndexLocation =
      gridRitem->Geo->DrawArgs["grid"].StartIndexLocation;
  gridRitem->BaseVertexLocation =
      gridRitem->Geo->DrawArgs["grid"].BaseVertexLocation;

  mRitemLayer[(int)RenderLayer::Opaque].push_back(gridRitem.get());
  mAllRitems.push_back(std::move(gridRitem));

  XMMATRIX brickTexTransform = XMMatrixScaling(1.5f, 2.0f, 1.0f);
  UINT objCBIndex = 5;
  for (int i = 0; i < 5; ++i) {
    auto leftCylRitem = std::make_unique<RenderItem>();
    auto rightCylRitem = std::make_unique<RenderItem>();
    auto leftSphereRitem = std::make_unique<RenderItem>();
    auto rightSphereRitem = std::make_unique<RenderItem>();

    XMMATRIX leftCylWorld = XMMatrixTranslation(-5.0f, 1.5f, -10.0f + i * 5.0f);
    XMMATRIX rightCylWorld =
        XMMatrixTranslation(+5.0f, 1.5f, -10.0f + i * 5.0f);

    XMMATRIX leftSphereWorld =
        XMMatrixTranslation(-5.0f, 3.5f, -10.0f + i * 5.0f);
    XMMATRIX rightSphereWorld =
        XMMatrixTranslation(+5.0f, 3.5f, -10.0f + i * 5.0f);

    XMStoreFloat4x4(&leftCylRitem->World, rightCylWorld);
    XMStoreFloat4x4(&leftCylRitem->TexTransform, brickTexTransform);
    leftCylRitem->ObjCBIndex = objCBIndex++;
    leftCylRitem->Mat = mMaterials["bricks0"].get();
    leftCylRitem->Geo = mGeometries["shapeGeo"].get();
    leftCylRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
    leftCylRitem->IndexCount =
        leftCylRitem->Geo->DrawArgs["cylinder"].IndexCount;
    leftCylRitem->StartIndexLocation =
        leftCylRitem->Geo->DrawArgs["cylinder"].StartIndexLocation;
    leftCylRitem->BaseVertexLocation =
        leftCylRitem->Geo->DrawArgs["cylinder"].BaseVertexLocation;

    XMStoreFloat4x4(&rightCylRitem->World, leftCylWorld);
    XMStoreFloat4x4(&rightCylRitem->TexTransform, brickTexTransform);
    rightCylRitem->ObjCBIndex = objCBIndex++;
    rightCylRitem->Mat = mMaterials["bricks0"].get();
    rightCylRitem->Geo = mGeometries["shapeGeo"].get();
    rightCylRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
    rightCylRitem->IndexCount =
        rightCylRitem->Geo->DrawArgs["cylinder"].IndexCount;
    rightCylRitem->StartIndexLocation =
        rightCylRitem->Geo->DrawArgs["cylinder"].StartIndexLocation;
    rightCylRitem->BaseVertexLocation =
        rightCylRitem->Geo->DrawArgs["cylinder"].BaseVertexLocation;

    XMStoreFloat4x4(&leftSphereRitem->World, leftSphereWorld);
    leftSphereRitem->TexTransform = MathHelper::Identity4x4();
    leftSphereRitem->ObjCBIndex = objCBIndex++;
    leftSphereRitem->Mat = mMaterials["mirror0"].get();
    leftSphereRitem->Geo = mGeometries["shapeGeo"].get();
    leftSphereRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
    leftSphereRitem->IndexCount =
        leftSphereRitem->Geo->DrawArgs["sphere"].IndexCount;
    leftSphereRitem->StartIndexLocation =
        leftSphereRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
    leftSphereRitem->BaseVertexLocation =
        leftSphereRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;

    XMStoreFloat4x4(&rightSphereRitem->World, rightSphereWorld);
    rightSphereRitem->TexTransform = MathHelper::Identity4x4();
    rightSphereRitem->ObjCBIndex = objCBIndex++;
    rightSphereRitem->Mat = mMaterials["mirror0"].get();
    rightSphereRitem->Geo = mGeometries["shapeGeo"].get();
    rightSphereRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
    rightSphereRitem->IndexCount =
        rightSphereRitem->Geo->DrawArgs["sphere"].IndexCount;
    rightSphereRitem->StartIndexLocation =
        rightSphereRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
    rightSphereRitem->BaseVertexLocation =
        rightSphereRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;

    mRitemLayer[(int)RenderLayer::Opaque].push_back(leftCylRitem.get());
    mRitemLayer[(int)RenderLayer::Opaque].push_back(rightCylRitem.get());
    mRitemLayer[(int)RenderLayer::Opaque].push_back(leftSphereRitem.get());
    mRitemLayer[(int)RenderLayer::Opaque].push_back(rightSphereRitem.get());

    mAllRitems.push_back(std::move(leftCylRitem));
    mAllRitems.push_back(std::move(rightCylRitem));
    mAllRitems.push_back(std::move(leftSphereRitem));
    mAllRitems.push_back(std::move(rightSphereRitem));
  }
}

void DxGraphics::DynamicCube_BuildFrameResources() {
  FrameResourceDesc desc = {};
  desc.PassCount = 7; // 需要6个用于渲染天空盒
  desc.ObjectCount = static_cast<UINT>(items_.size());
  desc.MaterialCount = static_cast<UINT>(materials_.size());

  for (int i = 0; i < FRAME_RESOURCE_COUNT; i++) {
    frame_resources_.push_back(
        std::make_unique<FrameResource>(device_.Get(), &desc));
  }
}

void DxGraphics::DynamicCube_BuildPSOs() {
  D3D12_GRAPHICS_PIPELINE_STATE_DESC opaquePsoDesc;

  //
  // PSO for opaque objects.
  //
  ZeroMemory(&opaquePsoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
  opaquePsoDesc.InputLayout = {mInputLayout.data(), (UINT)mInputLayout.size()};
  opaquePsoDesc.pRootSignature = mRootSignature.Get();
  opaquePsoDesc.VS = {
      reinterpret_cast<BYTE*>(mShaders["standardVS"]->GetBufferPointer()),
      mShaders["standardVS"]->GetBufferSize()};
  opaquePsoDesc.PS = {
      reinterpret_cast<BYTE*>(mShaders["opaquePS"]->GetBufferPointer()),
      mShaders["opaquePS"]->GetBufferSize()};
  opaquePsoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
  opaquePsoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
  opaquePsoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
  opaquePsoDesc.SampleMask = UINT_MAX;
  opaquePsoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
  opaquePsoDesc.NumRenderTargets = 1;
  opaquePsoDesc.RTVFormats[0] = mBackBufferFormat;
  opaquePsoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
  opaquePsoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
  opaquePsoDesc.DSVFormat = mDepthStencilFormat;
  ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(
      &opaquePsoDesc, IID_PPV_ARGS(&mPSOs["opaque"])));

  //
  // PSO for sky.
  //
  D3D12_GRAPHICS_PIPELINE_STATE_DESC skyPsoDesc = opaquePsoDesc;

  // The camera is inside the sky sphere, so just turn off culling.
  skyPsoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;

  // Make sure the depth function is LESS_EQUAL and not just LESS.
  // Otherwise, the normalized depth values at z = 1 (NDC) will
  // fail the depth test if the depth buffer was cleared to 1.
  skyPsoDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
  skyPsoDesc.pRootSignature = mRootSignature.Get();
  skyPsoDesc.VS = {
      reinterpret_cast<BYTE*>(mShaders["skyVS"]->GetBufferPointer()),
      mShaders["skyVS"]->GetBufferSize()};
  skyPsoDesc.PS = {
      reinterpret_cast<BYTE*>(mShaders["skyPS"]->GetBufferPointer()),
      mShaders["skyPS"]->GetBufferSize()};
  ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(
      &skyPsoDesc, IID_PPV_ARGS(&mPSOs["sky"])));
}

void DxGraphics::DrawSceneToCubeMap() {
  auto vp = mDynamicCubeMap->Viewport();
  mCommandList->RSSetViewports(1, &vp);
  auto sr = mDynamicCubeMap->ScissorRect();
  mCommandList->RSSetScissorRects(1, &sr);

  // Change to RENDER_TARGET.
  auto b1 = CD3DX12_RESOURCE_BARRIER::Transition(mDynamicCubeMap->Resource(),
      D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_STATE_RENDER_TARGET);
  mCommandList->ResourceBarrier(1, &b1);

  UINT passCBByteSize =
      dxutils::calc_constant_buffer_size(sizeof(PassConstants));

  // For each cube map face.
  for (int i = 0; i < 6; ++i) {
    // Clear the back buffer and depth buffer.
    mCommandList->ClearRenderTargetView(
        mDynamicCubeMap->Rtv(i), Colors::LightSteelBlue, 0, nullptr);
    mCommandList->ClearDepthStencilView(mCubeDSV,
        D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);

    // Specify the buffers we are going to render to.

    auto rtv = mDynamicCubeMap->Rtv(i);
    mCommandList->OMSetRenderTargets(1, &rtv, true, &mCubeDSV);

    // Bind the pass constant buffer for this cube map face so we use
    // the right view/proj matrix for this cube face.
    auto passCB = mCurrFrameResource->PassCB->Resource();
    D3D12_GPU_VIRTUAL_ADDRESS passCBAddress =
        passCB->GetGPUVirtualAddress() + (1 + i) * passCBByteSize;
    mCommandList->SetGraphicsRootConstantBufferView(1, passCBAddress);

    DrawRenderItems_V2(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Opaque]);

    mCommandList->SetPipelineState(mPSOs["sky"].Get());
    DrawRenderItems_V2(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Sky]);

    mCommandList->SetPipelineState(mPSOs["opaque"].Get());
  }

  // Change back to GENERIC_READ so we can read the texture in a shader.
  auto b2 = CD3DX12_RESOURCE_BARRIER::Transition(mDynamicCubeMap->Resource(),
      D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_GENERIC_READ);
  mCommandList->ResourceBarrier(1, &b2);
}


} // namespace ifire